#pragma once
#include <thread>
#include <functional>
#include <condition_variable>
#include <unordered_set>
#include "../ErrorLog.h"
#include "../Dispatcher.h"

#define CONNECT_SUCCESS		0
#define CONNECT_FAILED		1

struct OverlappedData;
struct sockaddr_in;
class TCPClient : public Dispatcher
{
public:

	TCPClient();

	virtual ~TCPClient();

	void SetContext(void* context);

	int Send(char* data, int size);

	void Disconnect();

	void AsyncConnect(const char* ip, int port, int timeout = -1);

	void InsertContext(OverlappedData* data);

	void RemoveContext(OverlappedData* data);

	bool GetRemoteAddr(sockaddr_in* out);

	bool GetLocalAddr(sockaddr_in* out);

	void SetRemoteAddr(sockaddr_in* in);

	void SetLocalAddr(sockaddr_in* in);

	template <class _Fx, class _Types>
	void RegisterConnectCompletedCallback(_Fx&& _Func, _Types&& _User) {
		onConnectCompleted = std::bind(_STD forward<_Fx>(_Func), _User, std::placeholders::_1, std::placeholders::_2);
	}

	template <class _Fx>
	void RegisterConnectCompletedCallback(_Fx&& _Func) {
		onConnectCompleted = std::bind(_STD forward<_Fx>(_Func), std::placeholders::_1);
	}

	void UnregisterConnectCompletedCallback() {
		onConnectCompleted = nullptr;
	}

	template <class _Fx, class _Types>
	void RegisterDisconnectedCallback(_Fx&& _Func, _Types&& _User) {
		onDisconnectedEvent = std::bind(_STD forward<_Fx>(_Func), _User);
	}

	template <class _Fx>
	void RegisterDisconnectedCallback(_Fx&& _Func) {
		onDisconnectedEvent = std::bind(_STD forward<_Fx>(_Func));
	}

	void UnregisterDisconnectedCallback() {
		onDisconnectedEvent = nullptr;
	}

	template <class _Fx, class _Types>
	void RegisterReceivedCallback(_Fx&& _Func, _Types&& _User) {
		onReceivedEvent = std::bind(_STD forward<_Fx>(_Func), _User, std::placeholders::_1, std::placeholders::_2);
	}

	template <class _Fx>
	void RegisterReceivedCallback(_Fx&& _Func) {
		onReceivedEvent = std::bind(_STD forward<_Fx>(_Func), std::placeholders::_1, std::placeholders::_2);
	}

	void UnregisterReceivedCallback() {
		onReceivedEvent = nullptr;
	}

private:

	void Run(std::string ip, int port, int timeout);

	int ConnectImp(uint64_t socket, const char* ip, int port, int timeout);

	void FreeAllContext();

private:

	std::function<void(int, std::string)> onConnectCompleted;
	std::function<void()> onDisconnectedEvent;
	std::function<void(char* data, int size)> onReceivedEvent;

public:

	void* context = nullptr;
	void* user = nullptr;

private:

	std::thread* threadHandle = nullptr;

	uint64_t socketHandle = 0;
	void* completionPort = NULL;
	OverlappedData* ctx = NULL;
	std::string errorStr;
	std::mutex mux;
	bool isClosed = false;
	std::unordered_set<OverlappedData*> sendList;

	char remoteAddr[32];
	char localAddr[32];
};

